#include <iostream>
#include <string>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>

#include "helper.hpp"
#include "interface.hpp"
#include "config_parser.hpp"

extern std::map<unsigned int, Base*> gids;

extern std::map<unsigned int, Base> vars;
extern std::map<unsigned int, Mode> modes;
extern std::map<unsigned int, Gsm> gsm_cmds;
extern std::map<unsigned int, Panel> panels;
extern std::map<unsigned int, Sensor> sensors;
extern std::map<unsigned int, Command> commands;
extern std::map<unsigned int, Timer> user_timers;
extern std::map<unsigned int, Fingerprint> fingerprint_cmds;
extern std::map<unsigned int, Function> functions;

struct timeval commands_read_timeout  = { .tv_sec = 0, .tv_usec = 50000 };

xmlpp::DomParser xml_doc;
extern pthread_mutex_t save_config_mutex;

void save_config()
{
    pthread_mutex_lock(&save_config_mutex);
    xml_doc.get_document()->write_to_file_formatted("config.xml");
    pthread_mutex_unlock(&save_config_mutex);
}

/* extract g[*]:value pair, and push it into vector with pairs. */
bool extract(const std::string& str, 
        std::vector< std::pair<unsigned int, std::string> >& vec)
{
    std::string tmp_str = str;
    std::vector<std::string> tokens;
    find_and_replace(tmp_str, "sleep", "g0");
    tokenize(tmp_str, " ", tokens);
    for(size_t i = 0; i < tokens.size(); i++){
        size_t pos = tokens[i].find_first_of(":");
        if(pos == std::string::npos){
            return false;
        }
        std::pair<unsigned int, std::string> tmp;
        tmp.first = atoi(tokens[i].substr(1, pos).c_str()); // excluding 'g'
        tmp.second = tokens[i].substr(pos + 1); 
        vec.push_back(tmp);
    } 
    return true;
}

/* extract g[*]:g[*] pair, and push it into vector with pairs. */
bool extract_connections(const std::string& str, 
        std::vector< std::pair<unsigned int, unsigned int> >& vec)
{
    std::vector<std::string> tokens;
    tokenize(str, " ", tokens);
    for(size_t i = 0; i < tokens.size(); i++){
        size_t pos = tokens[i].find_first_of(":");
        if(pos == std::string::npos){
            return false;
        }
        std::pair<unsigned int, unsigned int> tmp;
        tmp.first = atoi(tokens[i].substr(1, pos).c_str()); // excluding 'g'
        tmp.second = atoi(tokens[i].substr(pos + 1 + 1).c_str()); // excluding 'g'
        vec.push_back(tmp);
    } 
    return true;
}

bool extract_expr_true_false(std::string etf, Commands_info &cmd_inf)
{
    bool rv;
    size_t second_sharp;
    size_t third_sharp;

    std::string expr;
    std::string if_true;
    std::string if_false;

    second_sharp = etf.find_first_of('#', 1);
    if(second_sharp == std::string::npos){
        return false;
    }

    third_sharp = etf.find_first_of('#', second_sharp + 1);
    if(third_sharp == std::string::npos){
        return false;
    }

    expr = etf.substr(1, second_sharp - 1);
    if_true = etf.substr(second_sharp + 1, third_sharp - second_sharp - 1);
    if_false = etf.substr(third_sharp + 1);

    cmd_inf.expr = expr;
    rv = extract(if_true, cmd_inf.if_true);
    rv &= extract(if_false, cmd_inf.if_false);

    return rv;
}

void get_function(const xmlpp::Node* node)
{
    Function func;
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    func.true_cmds.type = EMPTY;
    func.false_cmds.type = EMPTY;

    if(nodeElement) {
        /* A panel Element node. */
		xmlpp::Attribute* attribute;
		attribute = nodeElement->get_attribute("name");
		if(attribute) {
			func.name = attribute->get_value();
		}
		attribute = nodeElement->get_attribute("gid");
		if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            func.gid = atoi(str.c_str());
		}
        attribute = nodeElement->get_attribute("expr");
        if(attribute) {
            func.expr  = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("true");
        if(attribute) {
            std::string tmp = attribute->get_value();
            if(tmp[0] == '#'){
                if(!extract_expr_true_false(tmp, func.true_cmds)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
                func.true_cmds.type = CONDITIONAL;
            } else {
                func.true_cmds.type = SAMPLE;
                if(!extract(tmp, func.true_cmds.sample)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
            }
        }
        attribute = nodeElement->get_attribute("false");
        if(attribute) {
            std::string tmp = attribute->get_value();
            if(tmp[0] == '#'){
                if(!extract_expr_true_false(tmp, func.false_cmds)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
                func.false_cmds.type = CONDITIONAL;
            } else {
                func.false_cmds.type = SAMPLE;
                if(!extract(tmp, func.false_cmds.sample)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
            }
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    /* Extracting 'g*' global IDs from the expr */
    extract_gids(func.expr, func.expr_gids);

    functions[func.gid] = func;
    gids[func.gid] = &functions[func.gid];
}

void get_var(const xmlpp::Node* node)
{
	const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    Base b;

	if(nodeElement) {
		/* A mode Element node. */
		xmlpp::Attribute* attribute;
		attribute = nodeElement->get_attribute("name");
		if(attribute) {
			b.name = attribute->get_value();
		}
		attribute = nodeElement->get_attribute("gid");
		if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            b.gid = atoi(str.c_str());
		}
		attribute = nodeElement->get_attribute("value");
		if(attribute) {
            b.attribute = attribute;
			b.value = attribute->get_value();
		}
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    vars[b.gid] = b;
    gids[b.gid] = &vars[b.gid];
}

void get_sensor(const xmlpp::Node* node, Panel &p)
{
    Sensor s;
    s.ivent_handler = 0;
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);

    if(nodeElement) {
        /* A panel Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("name");
        if(attribute) {
            s.name = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("gid");
        if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            s.gid = atoi(str.c_str());
        }
        attribute = nodeElement->get_attribute("id");
        if(attribute) {
            s.id = atoi(std::string(attribute->get_value()).c_str());
        }

        attribute = nodeElement->get_attribute("ivent_handler");
        if(attribute) {
            s.ivent_handler = atoi(std::string(attribute->get_value()).substr(1).c_str());
        }

        attribute = nodeElement->get_attribute("type");
        if(attribute) {
            std::string type;
            type = attribute->get_value();
            if(type == "contact")
                s.type = CONTACT;
            else if(type == "button")
                s.type = BUTTON;
            else if(type == "digital")
                s.type = DIGITAL;
            else {
                logger(SM_LOG_ERR,"ERROR: Unrecognized SENSOR type = %s, LINE = %d\n", 
                        type.c_str(), node->get_line());
                exit(-1);
            }
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    switch(s.type) {
        case CONTACT: 
            if(!(s.id >= 1 && s.id <= 16)) { 
                logger(SM_LOG_ERR,"ERROR: wrong sensor id = %d, contact sensor's id must"
                        " be between 1 <= id <= 16, config LINE = %d\n", 
                        s.id, node->get_line()); 
                exit(-1);
            }
            break;
        case BUTTON: 
            if(!(s.id >= 1 && s.id <= 12)) { 
                logger(SM_LOG_ERR,"ERROR: wrong sensor id = %d, button sensor's id must be"
                        " between 1 <= id <= 12, config LINE = %d\n", 
                        s.id, node->get_line()); 
                exit(-1);
            }
            break;
        case DIGITAL: 
            if(!(s.id >= 0 && s.id <= 255)) { 
                logger(SM_LOG_ERR,"ERROR: wrong sensor id = %d, digital sensor's id must"
                        " be between 0 <= id <= 255, config LINE = %d\n", 
                        s.id, node->get_line()); 
                exit(-1);
            }
            break;
        default:
            break;
    }

    sensors[s.gid] = s;
    gids[s.gid] = &sensors[s.gid];
    p.sensors.push_back(&sensors[s.gid]);
}

void get_fingerprint_cmd(const xmlpp::Node* node)
{
    Fingerprint f;
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);

    if(nodeElement) {
        /* A Command Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("name");
        if(attribute) {
            f.name = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("gid");
        if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            f.gid = atoi(str.c_str());
        }
        attribute = nodeElement->get_attribute("cmd_type");
        if(attribute) {
            std::string type = attribute->get_value();
            if(type == "IDENTIFY")
                f.cmd_type = FP_IDENTIFY;
            else if(type == "ENROLL")
                f.cmd_type = FP_ENROLL;
            else if(type == "DELETE")
                f.cmd_type = FP_DELETE;
            else if(type == "CHANGE_LEVEL")
                f.cmd_type = FP_CHANGE_LEVEL;
            else 
                logger(SM_LOG_ERR,"ERROR: Unrecognized fingerprint cmd type = %s\n",
                        type.c_str());
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    fingerprint_cmds[f.gid] = f;
    gids[f.gid] = &fingerprint_cmds[f.gid];
}

void get_gsm_cmd(const xmlpp::Node* node)
{
    Gsm g;
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);

    if(nodeElement) {
        /* A Command Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("name");
        if(attribute) {
            g.name = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("gid");
        if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            g.gid = atoi(str.c_str());
        }
        attribute = nodeElement->get_attribute("number");
        if(attribute) {
            g.number = attribute->get_value();
            if(g.number[0] == '(') {
                std::vector<unsigned int> tmp;
                extract_gids(g.number, tmp);
                replace_ids_to_values(tmp, g.number);
                g.number = g.number.substr(1, g.number.length() - 2);
            }
        }
        attribute = nodeElement->get_attribute("text");
        if(attribute) {
            g.text = attribute->get_value();
            if(g.text[0] == '(') {
                std::vector<unsigned int> tmp;
                extract_gids(g.text, tmp);
                replace_ids_to_values(tmp, g.text);
                g.text = g.text.substr(1, g.text.length() - 2);
            }
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    gsm_cmds[g.gid] = g;
    gids[g.gid] = &gsm_cmds[g.gid];
}

void get_panel(const xmlpp::Node* node)
{
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    Panel p;

    if(nodeElement) {
        /* A panel Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("name");
        if(attribute) {
            p.name = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("gid");
        if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            p.gid = atoi(str.c_str());
        }
        attribute = nodeElement->get_attribute("port");
        if(attribute) {
            p.port = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("init");
        if(attribute) {
            p.init_data = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("update");
        if(attribute) {
            p.update_data = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("timeout");
        if(attribute) {
            double f;
            std::string str = std::string(attribute->get_value()).c_str();
            if(str[0] == '(') {
                f = eval_expr2(str);
            } else {
                f = atof(str.c_str());
            }
            double fractional;
            double integral;
            fractional = modf(f, &integral);
            p.timeout.it_value.tv_sec = (int)integral;
            p.timeout.it_value.tv_nsec = (long)lrint((fractional) * 1000) * 1000000;
            p.timeout.it_interval.tv_sec = 0;
            p.timeout.it_interval.tv_nsec = 0;
        }
        attribute = nodeElement->get_attribute("read_timeout");
        if(attribute) {
            double f;
            std::string str = std::string(attribute->get_value()).c_str();
            f = atof(str.c_str());
            double fractional;
            double integral;
            fractional = modf(f, &integral);
            p.read_timeout.tv_sec = (int)integral;
            p.read_timeout.tv_usec = (long)lrint((fractional) * 1000) * 1000;
        }
    }

    /* Recurse through the panel's sensors. */
    const xmlpp::TextNode* nodeText;
    xmlpp::Node::NodeList::iterator iter;
    xmlpp::Node::NodeList node_list = node->get_children();
    for(iter = node_list.begin(); iter != node_list.end(); ++iter) {
        nodeText = dynamic_cast<const xmlpp::TextNode*>(*iter);
        // Skip empty lines and indents.
        if(nodeText && nodeText->is_white_space()){
            continue;
        }
        get_sensor(*iter, p);
    }

    panels[p.gid] = p;
    gids[p.gid] = &panels[p.gid];
}

void get_timer(const xmlpp::Node* node)
{
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    Timer t;

    if(nodeElement) {
        /* A Command Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("name");
        if(attribute) {
            t.name = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("gid");
        if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            t.gid = atoi(str.c_str());
        }
        attribute = nodeElement->get_attribute("state");
        if(attribute) {
            t.state = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("timeout");
        if(attribute){
            double f;
            t.value = attribute->get_value();
            std::string str = std::string(attribute->get_value()).c_str();
            if(str[0] == '(') {
                f = eval_expr2(str);
            } else {
                f = atof(str.c_str());
            }
            double fractional;
            double integral;
            fractional = modf(f, &integral);
            t.timeout.it_value.tv_sec = (int)integral;
            t.timeout.it_value.tv_nsec = (long)lrint((fractional) * 1000) * 1000000;
            t.timeout.it_interval.tv_sec = 0;
            t.timeout.it_interval.tv_nsec = 0;
        }
        attribute = nodeElement->get_attribute("actions");
        if(attribute) {
            if(!extract(attribute->get_value(), t.actions)) {
                logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                        node->get_line());
                exit(-1);
            }
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    /* Recurse through the mode's conditions. */
    const xmlpp::TextNode* nodeText;
    xmlpp::Node::NodeList::iterator iter;
    xmlpp::Node::NodeList node_list = node->get_children();
    for(iter = node_list.begin(); iter != node_list.end(); ++iter) {
        nodeText = dynamic_cast<const xmlpp::TextNode*>(*iter);
        // Skip empty lines and indents.
        if(nodeText && nodeText->is_white_space()){
            continue;
        }
        std::string node_name = (*iter)->get_name();
        if(node_name == "var"){
            get_var(*iter);
        } else if(node_name == "function"){
            get_function(*iter);
        } 
    }

    user_timers[t.gid] = t;
    gids[t.gid] = &user_timers[t.gid];
}

void get_commands_read_timeout(const xmlpp::Node* node)
{
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);

    if(nodeElement) {
        /* A panel Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("value");
        if(attribute) {
            double f;
            std::string str = std::string(attribute->get_value()).c_str();
            f = atof(str.c_str());
            double fractional;
            double integral;
            fractional = modf(f, &integral);
            commands_read_timeout.tv_sec = (int)integral;
            commands_read_timeout.tv_usec = (long)lrint((fractional) * 1000) * 1000;
        }
    }
}

void get_command(const xmlpp::Node* node)
{
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    Command c;

    if(nodeElement) {
        /* A Command Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("name");
        if(attribute) {
            c.name = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("gid");
        if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            c.gid = atoi(str.c_str());
        }
        attribute = nodeElement->get_attribute("mode");
        if(attribute) {
            std::string tmp = attribute->get_value();
            if(tmp == "slow"){
                c.mode = SLOW;
            }
        }
        attribute = nodeElement->get_attribute("port");
        if(attribute) {
            c.port = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("data_for_set");
        if(attribute) {
            c.data_for_set = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("data_for_get");
        if(attribute) {
            c.data_for_get = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("start_byte");
        if(attribute) {
            c.start_byte = atoi(std::string(attribute->get_value()).c_str());
            if(c.start_byte < 0) {
                logger(SM_LOG_ERR,"ERROR: wrong start byte = %d, LINE = %d\n", c.start_byte, node->get_line());
                exit(-1);
            }
        }
        attribute = nodeElement->get_attribute("bytes_to_get");
        if(attribute) {
            c.bytes_to_get = atoi(std::string(attribute->get_value()).c_str());
            if(c.bytes_to_get < 0 && c.bytes_to_get > 4) {
                logger(SM_LOG_ERR,"ERROR: wrong bytes_to_get = %d, LINE = %d\n", 
                        c.bytes_to_get, node->get_line());
                exit(-1);
            }
        }
        attribute = nodeElement->get_attribute("connections");
        if(attribute) {
            std::string connections = attribute->get_value();
            if(!extract_connections(connections, c.connections))
                logger(SM_LOG_ERR,"ERROR: syntax error in the command's connections attribute, LINE = %d\n",
                        node->get_line());
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    /* Extracting 'g*' global IDs from the data_for_set */
    extract_gids(c.data_for_set, c.data_gids);

    c.read_timeout = commands_read_timeout;

    for(size_t i = 0; i < c.connections.size(); i++) {
        if(c.gid == c.connections[i].first || c.gid == c.connections[i].second) {
            logger(SM_LOG_ERR,"ERROR: the [g%d] command's gid can't be in the part of the "
                    "connections\n", c.gid);
            exit(-1);
        }
    }

    commands[c.gid] = c;
    gids[c.gid] = &commands[c.gid];
}

void get_condition(const xmlpp::Node* node, Mode &m)
{
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    Condition cnd;
    cnd.true_cmds.type = EMPTY;
    cnd.false_cmds.type = EMPTY;

    if(nodeElement) {
        /* A panel Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("expr");
        if(attribute) {
            cnd.expr  = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("true");
        if(attribute) {
            std::string tmp = attribute->get_value();
            if(tmp[0] == '#'){
                if(!extract_expr_true_false(tmp, cnd.true_cmds)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
                cnd.true_cmds.type = CONDITIONAL;
            } else {
                cnd.true_cmds.type = SAMPLE;
                if(!extract(tmp, cnd.true_cmds.sample)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
            }
        }
        attribute = nodeElement->get_attribute("false");
        if(attribute) {
            std::string tmp = attribute->get_value();
            if(tmp[0] == '#'){
                if(!extract_expr_true_false(tmp, cnd.false_cmds)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
                cnd.false_cmds.type = CONDITIONAL;
            } else {
                cnd.false_cmds.type = SAMPLE;
                if(!extract(tmp, cnd.false_cmds.sample)) {
                    logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n", 
                            node->get_line());
                    exit(-1);
                }
            }
        }
        attribute = nodeElement->get_attribute("timeout");
        if(attribute){
            double f;
            std::string str = std::string(attribute->get_value()).c_str();
            if(str[0] == '(') {
                f = eval_expr2(str);
            } else {
                f = atof(str.c_str());
            }
            double fractional;
            double integral;
            fractional = modf(f, &integral);
            cnd.timeout.it_value.tv_sec = (int)integral;
            cnd.timeout.it_value.tv_nsec = (long)lrint((fractional) * 1000) * 1000000;
            cnd.timeout.it_interval.tv_sec = 0;
            cnd.timeout.it_interval.tv_nsec = 0;
        }
        attribute = nodeElement->get_attribute("step");
        if(attribute) {
            double f;
            std::string str = std::string(attribute->get_value()).c_str();
            if(str[0] == '(') {
                f = eval_expr2(str);
            } else {
                f = atof(str.c_str());
            }
            double fractional;
            double integral;
            fractional = modf(f, &integral);
            cnd.step_timeout.it_value.tv_sec = (int)integral;
            cnd.step_timeout.it_value.tv_nsec = (long)lrint((fractional) * 1000) * 1000000;
            cnd.step_timeout.it_interval.tv_sec = 0;
            cnd.step_timeout.it_interval.tv_nsec = 0;
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }
    m.cnds.push_back(cnd);
}

void get_mode(const xmlpp::Node* node)
{
    const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
    Mode m;
    m.working_count = 0;

    if(nodeElement) {
        /* A mode Element node. */
        xmlpp::Attribute* attribute;
        attribute = nodeElement->get_attribute("name");
        if(attribute) {
            m.name = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("gid");
        if(attribute) {
            std::string str = attribute->get_value();
            str.erase(0, 1); // removing 'g'
            m.gid = atoi(str.c_str());
        }
        attribute = nodeElement->get_attribute("state");
        if(attribute) {
            m.attribute = attribute;
            m.state = attribute->get_value();
        }
        attribute = nodeElement->get_attribute("actions");
        if(attribute) {
            if(!extract(attribute->get_value(), m.actions)) {
                logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                        node->get_line());
                exit(-1);
            }
        }
    } else {
        logger(SM_LOG_ERR,"ERROR: Unrecognized XML configuration, LINE = %d\n",
                node->get_line());
        exit(-1);
    }

    /* Recurse through the mode's conditions. */
    const xmlpp::TextNode* nodeText;
    xmlpp::Node::NodeList::iterator iter;
    xmlpp::Node::NodeList node_list = node->get_children();
    for(iter = node_list.begin(); iter != node_list.end(); ++iter) {
        nodeText = dynamic_cast<const xmlpp::TextNode*>(*iter);
        // Skip empty lines and indents.
        if(nodeText && nodeText->is_white_space()){
            continue;
        }
        std::string node_name = (*iter)->get_name();
        if(node_name == "var"){
            get_var(*iter);
        } else if(node_name == "function"){
            get_function(*iter);
        } else {
            get_condition(*iter, m);
        }
    }

    modes[m.gid] = m;
    gids[m.gid] = &modes[m.gid];
}

void parse_config(std::string xml_filepath)
{
    const xmlpp::Node* node;
    const xmlpp::TextNode* nodeText;
    xmlpp::Node::NodeList node_list, l;
    xmlpp::Node::NodeList::iterator iter, it;
    std::string node_name;

    xml_doc.set_validate(); 
    xml_doc.parse_file(xml_filepath);
    node = xml_doc.get_document()->get_root_node();

    /* Get the "panels" and recurse through it's child. */
    node_list = node->get_children();
    for(iter = node_list.begin(); iter != node_list.end(); ++iter) {
        nodeText = dynamic_cast<const xmlpp::TextNode*>(*iter);
        // Skip empty lines and indents.
        if(nodeText && nodeText->is_white_space()){
            continue;
        }
        /* Recurse through it's child. */
        l = (*iter)->get_children();
        for(it = l.begin(); it != l.end(); ++it) {
            nodeText = dynamic_cast<const xmlpp::TextNode*>(*it);
            // Skip empty lines and indents.
            if(nodeText && nodeText->is_white_space()){
                continue;
            }
            node_name = (*it)->get_name();
            if(node_name == "panel"){
                get_panel(*it);
            } else if(node_name == "command"){
                get_command(*it);
            } else if(node_name == "mode"){
                get_mode(*it);
            } else if(node_name == "var"){
                get_var(*it);
            } else if(node_name == "timer"){
                get_timer(*it);
            } else if(node_name == "gsm_cmd"){
                get_gsm_cmd(*it);
            } else if(node_name == "fingerprint_cmd"){
                get_fingerprint_cmd(*it);
            } else if(node_name == "function"){
                get_function(*it);
            } else if(node_name == "commands_read_timeout"){
                get_commands_read_timeout(*it);
            }
        }
    }
}
